제공된 데이터의 컬럼별 정보¶
컬럼명/ 정보 YM : 기준년월
SIDO : 지역대분류명
SIGUNGU : 지역중분류명
FranClass : 소상공인구분
Type : 업종명
Time : 시간대
TotalSpent : 총사용금액
DisSpent : 재난지원금 사용금액
NumOfSpent : 총 이용건수
NumOfDisSpent : 총 재난지원금 이용건수
POINT_X, POINT_Y : X,Y 좌표
import plotly.io as pio
pio.renderers.default = 'notebook_connected'
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import csv
from matplotlib import font_manager, rc
from datetime import datetime
import plotly.figure_factory as ff
from pyproj import Proj, Transformer
import plotly.express as px
import plotly.graph_objects as go
import seaborn as sns
sns.set_style('whitegrid')
import warnings
warnings.filterwarnings("ignore")
from plotly.subplots import make_subplots
import plotly.graph_objects as go
# korean setting
import platform
from matplotlib import font_manager, rc
plt.rcParams['axes.unicode_minus'] = False
if platform.system() == 'Darwin':
rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)
# 시작전 데이터 로딩
# df7의 X,Y 컬럼은 불필요 함으로 삭제
for n in range(5, 9):
globals()["df" + str(n)] = pd.read_csv("KRI-DAC_Jeju_data" + str(n)+".txt", sep=',')
df7.drop(columns = ['X', 'Y'], inplace = True)
기관_기업 = ['초중고교육기관', '공공요금', '조세서비스'] #공공기업/기관
음식점_식품 = ['일반한식', '단란주점', '주점','스넥', '서양음식', '일식회집', '기타음료식품', '중국음식', '유흥주점',
'인삼제품', '홍삼제품', '농협하나로클럽', '기타건강식', '유류판매', '농축협직영매장', '구내매점', '칵테일바', '정육점',
'주류판매점', '제과점', '미곡상']
패션_뷰티 = ['가방', '맞춤복점', '미용재료', '캐쥬얼의류', '양품점', '시계', '안경', '단체복', '아동의류', '신발', '기타의류',
'내의판매점', '민예공예품', '스포츠의류', '기타잡화', '악세사리', '정장', '피부미용실', '화장품']
취미_오락 = ['노래방', '당구장', '문화취미기타', '기타서적문구', 'DVD음반테이프판매', '화랑', '컴퓨터학원', '전문서적', '화방표구점',
'악기점', '예체능학원', '학원(회원제형태)', '외국어학원', '기능학원', '기타교육', '영화관', '헬스크럽', '독서실',
'애완동물', '볼링장']
_1차산업 = ['기타농업관련']
_2차산업 = ['기타직물', '기타광학품', '수입자동차', '기타비영리유통', '귀금속', '사무기기', '과학기자재', '윤활유전문판매', '통신기기',
'한약방', '카메라', '정수기', 'CATV', '기타사무용', '이륜차판매', '중고자동차', '철제가구', '일반가구',
'카페트커텐천막', '컴퓨터', '냉열기기', '소프트웨어', '유리', '자동차시트타이어', '기타전기제품',
'기타운송', '연쇄점', '옷감직물', '조명기구', '기타연료', '자동차부품', '기타가구', '비료농약사료종자', '농기계', '보일러펌프',
'페인트', '건축요업품', '기타건축자재', '목재석재철물', '기계공구', '의료용품', '기타유통업', '화물운송', 'LPG', '제약회사']
기타서비스업 = ['출판인쇄물', '서적출판(회원제형태)', '정보서비스', '사무서비스(회원제형태)', '기타자동차서비스', '사무통신기기수리', '법률회계서비스', '손해보험',
'신변잡화수리','기타보험', '중장비수리', '부동산중개임대', '견인서비스', '가정용품수리', '카인테리어', '기타수리서비스',
'인테리어', '기타용역서비스', '위탁급식업', '기타의료기관및기기', '가례서비스', '종합용역', '보관창고업', '부동산분양',
'기타대인서비스', '자동차정비', '사무서비스']
레져 = ['기타레져업', '골프경기장', '종합레져타운', '수영장', '레져업소(회원제형태)', '레져용품수리', '골프연습장', '스포츠레져용품',
'골프용품', '테니스장']
생활 = ['일반서적', '동물병원','산후조리원','편의점', '슈퍼마켓', '농축수산품', '유아원', '세탁소', '주차장', '주유소', '골동품점', '대학등록금','정기간행물',
'제화점', '건강식품(회원제형태)', '치과병원', '기타교통수단', '성인용품점', '치과의원', '병원', '주방용구', '완구점', '건강진단',
'한의원', '주방용식기', '문구용품', '이용원','가전제품', '택시', '세차장','인터넷종합Mall', '침구수예점', '사진관', '미용원',
'인터넷Mall', '대형할인점', '의원', '사우나', '종합병원', '약국', '안마스포츠마사지', '보습학원', '학습지교육']
관광= ['기타숙박업', '콘도', '여객선', '수족관', '티켓', '면세점', '렌트카', '기념품점','1급호텔', '관광여행', '항공사', '2급호텔',
'화원', '특급호텔']
기타 = [ '기타업종', '상품권', '기타회원제형태업소']
dae_list = ['기관_기업', '패션_뷰티', '음식점_식품', '취미_오락', '1차산업', '2차산업', '기타서비스업', '레져', '생활', '관광', '기타']
# 데이터 별 대분류 칼럼 추가
def dae_type(x):
for num, i in enumerate([기관_기업, 패션_뷰티, 음식점_식품, 취미_오락, _1차산업, _2차산업, 기타서비스업, 레져, 생활, 관광, 기타]):
if x in i:
return dae_list[num]
return None
df5['dae_type'] = df5['Type'].apply(dae_type)
df6['dae_type'] = df6['Type'].apply(dae_type)
df7['dae_type'] = df7['Type'].apply(dae_type)
df8['dae_type'] = df8['Type'].apply(dae_type)
#### 이후 대분류 포함 데이터 로딩용
df5_cat = pd.read_csv('df5.csv')
df6_cat = pd.read_csv('df6.csv')
df7_cat = pd.read_csv('df7.csv')
df8_cat = pd.read_csv('df8.csv')
df_all = pd.concat([df5_cat,df6_cat,df7_cat,df8_cat])
def set_time_category(time_str):
if time_str == "x시":
return "무승인거래"
else:
time = int(time_str[:2])
if time < 2:
return "심야"
elif time < 6:
return "새벽"
elif time < 11:
return "오전"
elif time < 15:
return "점심"
elif time < 18:
return "오후"
elif time < 22:
return "저녁"
else:
return "심야"
def set_time_cat_order(time_cat):
time_dict = {
"새벽" : 0,
"오전" : 1,
"점심" : 2,
"오후" : 3,
"저녁" : 4,
"심야" : 5,
"무승인거래" : 6
}
return time_dict[time_cat]
df_all["Time_cat"] = df_all["Time"].apply(set_time_category)
이용된 외부데이터
df_agg = df_all.groupby(['YM', 'SIGUNGU']).agg({"TotalSpent":"sum", "DisSpent":"sum", "NumofSpent":"sum", "NumofDisSpent":"sum"}).reset_index()
df_agg
sum_by_month= df_agg.groupby(['YM']).agg({"TotalSpent":"sum", "DisSpent":"sum"}).reset_index()
sum_by_sigun=df_agg.groupby(['SIGUNGU']).agg({"TotalSpent":"sum", "DisSpent":"sum"}).reset_index()
sum_by_time=df_all.groupby(['Time']).agg({"TotalSpent":"sum", "DisSpent":"sum"}).reset_index()
sum_by_type=df_all.groupby(['dae_type']).agg({"TotalSpent":"sum", "DisSpent":"sum"}).sort_values(by="TotalSpent").reset_index()
colors = px.colors.sequential.Plasma + px.colors.sequential.Oryel + px.colors.sequential.Aggrnyl + px.colors.sequential.Blues
fig = make_subplots(rows=2, cols=2,
subplot_titles=("월별 소비 합계(총 소비 & 재난지원금)", "시별 소비 합계", "시간별 소비 합계", "업종별 소비 합계(총 소비 & 재난지원금)"))
fig.add_trace(
go.Bar(x=sum_by_month["YM"].astype(str), y=sum_by_month["TotalSpent"],
marker=dict(color=colors)), 1, 1
)
fig.add_trace(
go.Bar(x=sum_by_month["YM"].astype(str), y=sum_by_month["DisSpent"],
marker=dict(color=colors)), 1, 1
)
fig.add_trace(
go.Bar(x=sum_by_sigun["SIGUNGU"], y=sum_by_sigun["TotalSpent"],
marker=dict(color=colors)), 1, 2
)
fig.add_trace(
go.Bar(x=sum_by_time["Time"], y=sum_by_time["TotalSpent"],
marker=dict(color=colors)), 2, 1
)
fig.add_trace(
go.Bar(x=sum_by_type["dae_type"], y=sum_by_type["TotalSpent"],
marker=dict(color=colors)), 2, 2
)
fig.add_trace(
go.Bar(x=sum_by_type["dae_type"], y=sum_by_type["DisSpent"],
marker=dict(color=colors)), 2, 2
)
fig.update_layout(height=600, width=1000, title_text="기본 분석")
fig.show()
# 월별 업종 대분류별 소비
df_by_month = df_all.groupby(['YM', 'dae_type']).agg({"TotalSpent":"sum", "DisSpent":"sum"}).sort_values(by=['YM', 'TotalSpent']).reset_index()
fig = go.Figure()
for i, month in enumerate(df_by_month["YM"].unique()):
df = df_by_month[df_by_month['YM'] == month]
row = i//2 + 1
col = i%2 + 1
fig.add_trace(
go.Bar(x=df["dae_type"], y=df["TotalSpent"],
name=str(month),
marker=dict(color=colors[i+5]))
)
fig.update_layout(height=400, width=1000, title_text="기본 분석")
fig.show()
이와 같은 특징들을 중심으로 특징들의 대한 이유 및 세부 사항을 분석해보겠음
# 월별 업종 대분류별 재난지원금 소비
df_by_month = df_all.groupby(['YM', 'dae_type']).agg({"TotalSpent":"sum", "DisSpent":"sum"}).sort_values(by=['YM', 'TotalSpent']).reset_index()
fig = go.Figure()
for i, month in enumerate(df_by_month["YM"].unique()):
df = df_by_month[df_by_month['YM'] == month]
row = i//2 + 1
col = i%2 + 1
fig.add_trace(
go.Bar(x=df["dae_type"], y=df["DisSpent"],
name=str(month),
marker=dict(color=colors[i+5]))
)
fig.update_layout(height=400, width=1000, title_text="재난지원금 기본 분석")
fig.show()
def fund_analysis(type_name):
#대분류 업종 내, 소분류 업종 필터링
df_sh = df_all[df_all['dae_type'] == type_name].groupby(['Type']).agg({"TotalSpent":"sum", "DisSpent":"sum"})
df_sh = df_sh.sort_values(by=["TotalSpent"], ascending=False).reset_index()
# 대분류 업종내, 소비 상위 10위 업종의 소상공인 구분 필터링
df_fc = df_all[df_all['dae_type'] == type_name].groupby(['Type', "FranClass"]).agg({"TotalSpent":"sum", "DisSpent":"sum"}).reset_index()
df_fc = df_fc[df_fc['Type'].isin(df_sh["Type"][:10])]
df = pd.DataFrame(columns=["Type", "FranClass", "TotalSpent", "DisSpent"])
for t in df_sh["Type"][:10]:
df_1 = df_fc[df_fc["Type"] == t]
df = pd.concat([df,df_1])
df_fc = df
# 각 그래프 시각화
fig = make_subplots(rows=2, cols=1,
subplot_titles=( type_name + "업종 소비 분석", "상위 10위 업종 소상공인구분"))
fig.add_trace(
go.Bar(x=df_sh["Type"], y=df_sh["TotalSpent"],
name="총 사용금액",
marker=dict(color=colors[5])),
1, 1)
fig.add_trace(
go.Bar(x=df_sh["Type"], y=df_sh["DisSpent"],
name="재난지원금 사용금액",
marker=dict(color=colors[8])),
1, 1)
for i,fc in enumerate(["영세", "중소", "중소1", "중소2", "일반"]):
df = df_fc[df_fc["FranClass"] == fc]
fig.add_trace(
go.Bar(x=df["Type"], y=df["TotalSpent"],
name=fc,
#marker=dict(color=colors[i])
)
,2, 1
)
fig.update_layout(height=1000, width=1000, title_text= type_name + "업종 소비 분석")
return fig
fig = fund_analysis("생활")
fig.show()
[생활 업종 중 가장 많은 소비를 차지하는 업종은 슈퍼마켓, 편의점, 대형할인점, 농축수산점 등 대형 생활용품/식품 판매점]
fig = fund_analysis("음식점_식품")
fig.show()
[음식점/식품 업종 중 가장 많은 소비를 차지하는 업종은 압도적으로 일반한식]
# 5~8월 에서 소비차이 계산
df = df_all[df_all['dae_type'] == "음식점_식품"].groupby(['YM','Type']).agg({"TotalSpent":"sum", "DisSpent":"sum"}).reset_index()
df = df.pivot_table(values="TotalSpent", index="Type", columns="YM").reset_index(drop=False)
df["diff"] = df[202008] - df[202005]
# 소비 증가량 시각화
fig = go.Figure()
df_sh = df.sort_values(by="diff", ascending=False)
fig.add_trace(
go.Bar(x=df_sh["Type"], y=df_sh["diff"],
name="총 사용금액",
marker=dict(color=colors)),
)
fig.update_layout(height=400, width=1000, title_text="음식점_식품 업종 5~8월 소비 증가량")
fig.show()
[5월에서 8월까지 음식점/식품 소비 증가의 대부분은 일반한식, 서양음식, 일식횟집 등 식당]
fig = fund_analysis("관광")
fig.show()
[면제점과 특급호텔, 콘도 등 숙박 업소들이 관광 업종에서 대부분을 차지]
df = df_all[df_all['dae_type'] == "관광"].groupby(['YM','Type']).agg({"TotalSpent":"sum", "DisSpent":"sum"}).reset_index()
df = df.pivot_table(values="TotalSpent", index="Type", columns="YM").reset_index(drop=False)
df["diff"] = df[202008] - df[202005]
fig = go.Figure()
df_sh = df.sort_values(by="diff", ascending=False)
fig.add_trace(
go.Bar(x=df_sh["Type"], y=df_sh["diff"],
name="총 사용금액",
marker=dict(color=colors)),
)
fig.update_layout(height=400, width=1000, title_text="관광 업종 5~8월 소비 증가량")
fig.show()
[가장 많이 소비가 증가한 업종은 콘도와 면세점. 관광여행, 여객선, 수족관 등 전통 관광 업종은 증가 약세]
df = df_all.groupby(['Time_cat']).agg({"TotalSpent":"sum", "NumofSpent":"sum", "DisSpent":"sum"}).reset_index()
df['Time_cat_order'] = df['Time_cat'].apply(set_time_cat_order)
df = df.sort_values(by="Time_cat_order")
# 새벽, 오전, 점심, 오후, 저녁, 심야로 그룹핑 하여 사용금액과 이용건수를 비교
fig = make_subplots(rows=1, cols=2,
subplot_titles=("시간대별 사용금액", "시간대별 이용건수"))
fig.add_trace(
go.Bar(x=df["Time_cat"], y=df["TotalSpent"],
name="시간별 사용금액",
marker=dict(color=colors[5])),
1, 1,
)
fig.add_trace(
go.Bar(x=df["Time_cat"], y=df["NumofSpent"],
name="시간별 이용건수",
marker=dict(color=colors[8])),
1, 2,
)
fig.update_layout(height=300, width=1000, title_text="시간대별 소비 분석")
fig.show()
[기본 분석에서 본대로 점심, 오후, 저녁에 소비금액과 이용건수가 집중되어 있는 것을 볼 수 있음]
df = df_all.groupby(['Type','Time_cat']).agg({"TotalSpent":"sum", "NumofSpent":"sum", "DisSpent":"sum"}).reset_index()
# 무승인거래 : 별도 승인없이 결제되는 건(SMS자동결제, 기내 면세점 등)
# 시간대별 소비금액 상위 10개 업종을 골라 소비 금액 시각화
fig = make_subplots(rows=7, cols=1,
subplot_titles=(
"새벽 (02~05시)",
"오전 (06~10시)",
"점심 (11~14시)",
"오후 (15~17시)",
"저녁 (18~21시)",
"심야 (22~01시)",
"무승인거래"
))
for i, time_cat in enumerate(['새벽', "오전", "점심", "오후", "저녁", "심야", "무승인거래"]):
df_time_cat = df[df["Time_cat"] == time_cat].sort_values(by="TotalSpent", ascending=False)[:10]
fig.add_trace(
go.Bar(x=df_time_cat["Type"], y=df_time_cat["TotalSpent"],
name= time_cat + "사용금액",
marker=dict(color=colors[5+i])),
i+1, 1,
)
fig.update_layout(height=1000, width=1000, title_text="")
fig.show()
[식사 시간대는 일반한식, 오전/오후는 필수 소비재, 심야/새벽은 주점들의 소비 강세 확인]
# 클러스터링을 위한 데이터셋 생성
# 업종별 0~23시까지 데이터셋 생성 및 null value는 0으로 설정
times = list(df_all["Time"].unique())
types = list(df_all["Type"].unique())
item = [times, types]
from itertools import product
df_temp = pd.DataFrame(product(*item), columns=["Time", "Type"])
# 기존데이터와 merge
df = pd.merge(df_all, df_temp, on=["Time", "Type"], how='right')
df = df[~(df["Time"]=='x시')] # 무승인거래 제거
df = df.fillna(0)
# 소상공인구분 없이 그룹핑
df = df.groupby(["Type", "Time"]).agg({"TotalSpent":"sum",
"DisSpent":"sum",
"NumofSpent":"sum",
}).reset_index()
# 시각화를 위한 스케일링 함수
def scale_data(df, target_col):
df[target_col + "_scaled"] = np.zeros(df.shape[0])
for a_type in df['Type'].unique():
temp = df[df['Type']==a_type].copy()
temp[target_col+"_scaled"] = temp[target_col].transform(lambda x: (x-x.min())/(x.max() - x.min()))
index = temp.index
df.loc[index, target_col+"_scaled"] = temp[target_col+"_scaled"]
return df
# 상관관계로 클러스터링을 하기 위해 피봇 테이블 생성 및 상관관계 계산
df_pivot = pd.pivot(df, index="Time", columns="Type", values="TotalSpent" )
df_pivot_corr = df_pivot.corr()
# hierarchical clustering 이용
import scipy.cluster.hierarchy as spc
pdist = spc.distance.pdist(df_pivot_corr)
linkage = spc.linkage(pdist, method="ward")
idx = spc.fcluster(linkage, 0.30 * pdist.max(), "distance")
cluster = pd.DataFrame({"Type":df_pivot_corr.index, "Group":idx})
# Dendrogram을 통해 적절한 클러스터 개수 측정
# 13개
fig = ff.create_dendrogram(linkage, color_threshold=135)
fig.update_layout(width=800, height=500)
fig.show()
import scipy.cluster.hierarchy as spc
pdist = spc.distance.pdist(df_pivot_corr)
linkage = spc.linkage(pdist, method="ward")
idx = spc.fcluster(linkage, 0.30 * pdist.max(), "distance")
cluster = pd.DataFrame({"Type":df_pivot_corr.index, "Group":idx})
cluster['Group'].unique()
df_scaled = scale_data(df, "TotalSpent")
# 클러스터 그룹별로 시각화
fig, ax = plt.subplots(13, 1, figsize=(20, 70))
for i in range(1, 14):
cluster_kind = list(cluster[cluster['Group']==i]['Type'])
temp = df_scaled[df_scaled['Type'].isin(cluster_kind)]
sns.lineplot(x=temp["Time"], y=temp["TotalSpent_scaled"], hue=temp["Type"], ax=ax[i-1])
ax[i-1].legend().set_visible(True)
ax[i-1].set_title(i, fontsize=16)
ax[i-1].set_ylabel("TotalSpent", fontsize=16)
fig, ax = plt.subplots(7, 2, figsize=(15, 18))
for i in range(1, 14):
col = (i - 1) // 7
row = (i-1) % 7
cluster_kind = list(cluster[cluster['Group']==i]['Type'])
temp = df_scaled[df_scaled['Type'].isin(cluster_kind)]
sns.lineplot(x=temp["Time"], y=temp["TotalSpent_scaled"], ax=ax[row, col],
palette=sns.light_palette("royalblue", n_colors=temp["Type"].nunique()))
ax[row, col].set_title(i, fontsize=16)
ax[row, col].set_xticklabels(["00시"]+['']*7+["08시"]+['']*7+['16시']+['']*6+["23시"])
ax[row, col].set_xlabel('')
ax[row, col].set_ylabel("TotalSpent", fontsize=16)
graph_shapes = ["점심전후형_1", "점심전후형_2", "오전형_1", "오후형_1", "오후형_2", "점심전후형_3", "오후형_3", "저녁심야형_1", "저녁심야형_2",
"점심저녁형_1", "NA", "점심형_1", "NA"]
cluster['Shape'] = cluster["Group"].apply(lambda x: graph_shapes[x-1])
cluster_filtered = cluster[cluster['Shape'] != "NA"]
# 해당 클러스트들을 특성 별로 나열
graphs_order = ["오전형_1", "점심전후형_1", "점심전후형_2", "점심전후형_3", "점심형_1", "오후형_1", "오후형_2", "오후형_3", "점심저녁형_1", "저녁심야형_1", "저녁심야형_2"]
fig, ax = plt.subplots(6, 2, figsize=(15, 18))
for i in range(1, 12):
if i > 5:
index = i+1
else:
index = i
col = (index - 1) // 6
row = (index-1) % 6
cluster_kind = list(cluster_filtered[cluster_filtered['Shape']==graphs_order[i-1]]['Type'])
temp = df_scaled[df_scaled['Type'].isin(cluster_kind)]
sns.lineplot(x=temp["Time"], y=temp["TotalSpent_scaled"], ax=ax[row, col], hue=temp['Type'],
palette=sns.light_palette("royalblue", n_colors=temp["Type"].nunique()))
ax[row, col].set_title(graphs_order[i-1], fontsize=16)
ax[row, col].set_xticklabels([])
ax[row, col].set_xlabel('')
ax[row, col].legend().set_visible(False)
ax[row, col].set_ylabel("TotalSpent", fontsize=16)
df_type_sum = df_all.groupby(["Type"]).agg({"TotalSpent":"sum"}).reset_index()
# 해당 클러스터별 소비가 높은 업종 상위 10개 나열
for a_group in graphs_order:
types_in_group = cluster[cluster["Shape"]==a_group]['Type']
temp_df = df_type_sum[df_type_sum["Type"].isin(types_in_group)]
temp_df = temp_df.sort_values(by="TotalSpent", ascending=False)
types_list = temp_df['Type'].values
if len(types_list) < 10:
print(a_group + ":")
print(*types_list, sep=', ')
print("-"*120)
else:
print(a_group + ":")
print(*types_list[:10], sep=', ')
print("-"*120)
각 클러스터별 주요 특징들을 보자면
# UTMK 좌표를 WGS84로 바꾸어줍니다
proj_UTMK = Proj(init = 'epsg:5178')
proj_WGS84 = Proj(init= 'epsg:4326')
transformer = Transformer.from_proj(proj_UTMK, proj_WGS84)
points = []
for index, item in df_all[['POINT_X', 'POINT_Y']].iterrows():
points.append((item.POINT_X, item.POINT_Y))
latlist, lnglist = [], []
for pt in transformer.itransform(points):
latlist.append(pt[1])
lnglist.append(pt[0])
df_all['Latitude'] = latlist
df_all['Longitude'] = lnglist
# use kakao open API to get address and x, y coordinates
import requests
lat = df_all['Latitude'].values[0]
lon= df_all['Longitude'].values[0]
url = f"https://dapi.kakao.com/v2/local/geo/coord2regioncode.json?x={lon}&y={lat}"
headers = {
"Authorization": "KakaoAK"
}
requests.get(url, headers = headers).json()
# 행정동 주소 가져오기 위한 함수
def get_address(lon, lat, headers):
url = f"https://dapi.kakao.com/v2/local/geo/coord2regioncode.json?x={lon}&y={lat}"
address = requests.get(url, headers = headers).json()["documents"]
return address[0]
# 복사본 데이터 셋을 생성 후 중복되는 위도, 경도를 제거
df = df_all.copy()
df["lon_lat"] = df["Longitude"].astype(str) + df["Latitude"].astype(str)
df = df.drop_duplicates("lon_lat")
len(df)
# 중복 없는 위도, 경도 콤비네이션에 대한 행정동 주소 가져오기
headers = {
"Authorization": "KakaoAK 8bcdbebf8e1c886ae09110fdfc0afd39"
}
gu = []
dong = []
li = []
for index, item in df[["Latitude", "Longitude"]].iterrows():
lon = item.Longitude
lat = item.Latitude
address = get_address(lon, lat, headers)
gu.append(address['region_2depth_name'])
dong.append(address['region_3depth_name'])
li.append(address['region_4depth_name'])
len(gu)
# 가져온 행정동 주소를 원래 데이터 셋과 merge 해주기
df["Gu"] = gu
df["Dong"] = dong
df["Li"] = li
df = df[['Latitude', "Longitude", "Gu", "Dong", "Li"]]
df_geo = pd.merge(df_all, df, on=["Latitude", "Longitude"], how="left")
df_geo.to_csv("df_geo.csv")
df_geo.head()
# 시 별로 소비금액 상위 15곳만 시각화
df = df_geo.groupby(["Gu", "Dong"]).agg({
"TotalSpent":"sum", "DisSpent":"sum", "NumofSpent":"sum", "Latitude":"mean", "Longitude":"mean"}).reset_index()
df = df.sort_values(by=["Gu","TotalSpent"], ascending=False)
df_seogui = df[df["Gu"]=="서귀포시"][:15]
df_jejusi = df[df["Gu"]=="제주시"][:15]
fig = make_subplots(rows=2, cols=1,
subplot_titles=(
"제주시 동별 소비금액",
"서귀포시 동별 소비금액",
))
fig.add_trace(
go.Bar(x=df_jejusi["Dong"], y=df_jejusi["TotalSpent"],
name="제주시 동별 소비금액",
marker=dict(color=colors)),
1, 1,
)
fig.add_trace(
go.Bar(x=df_jejusi["Dong"], y=df_jejusi["DisSpent"],
name="제주시 동별 재난지원금 소비금액",
marker=dict(color=colors)),
1, 1,
)
fig.add_trace(
go.Bar(x=df_seogui["Dong"], y=df_seogui["TotalSpent"],
name="서귀포시 동별 소비금액",
marker=dict(color=colors)),
2, 1,
)
fig.add_trace(
go.Bar(x=df_seogui["Dong"], y=df_seogui["DisSpent"],
name="서귀포시 동별 재난지원금 소비금액",
marker=dict(color=colors)),
2, 1,
)
fig.update_layout(height=500, width=1000, title_text="시별 소비금액 상위 15개 동")
fig.show()
# 제주시와 서귀포시 상위 15곳를 지도위에 시각화
fig = px.density_mapbox(df, lat=[], lon=[], z=[], radius=4,
center=dict( lat=df.Latitude.median(),
lon=df.Longitude.median()),
mapbox_style="open-street-map"
)
fig.add_trace(go.Scattermapbox(lat=df_jejusi.Latitude, lon=df_jejusi.Longitude,
opacity = 0.75,
marker = dict(size = df_jejusi.TotalSpent / max(df_jejusi.TotalSpent) * 50,
color = 'coral',
#line = dict(color='DarkSlateGrey', width=2)
),
text = [df_jejusi["Dong"].values[i] +":" + str(i+1) + "위 \n 소비금액(억원):"+str(round(df_jejusi['TotalSpent'].values[i]/100000000, 2)) for i in range(15)],
name = '제주시 소비금액 상위 15개 동'
))
fig.add_trace(go.Scattermapbox(lat=df_seogui.Latitude, lon=df_seogui.Longitude,
opacity = 0.75,
marker = dict(size = df_seogui.TotalSpent / max(df_jejusi.TotalSpent) * 50,
color = 'blue',
#line = dict(color='DarkSlateGrey', width=2)
),
text = [df_seogui["Dong"].values[i] +":" + str(i+1) + "위 \n 소비금액(억원):"+str(round(df_seogui['TotalSpent'].values[i]/100000000, 2)) for i in range(15)],
name = '서귀포시 소비금액 상위 15개 동'
))
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()
# 동별로 소비금액 상위 5개 업종 그래프화
df = df_geo.groupby(["Gu", "Dong", "Type"]).agg({
"TotalSpent":"sum", "DisSpent":"sum", "NumofSpent":"sum", "Latitude":"mean", "Longitude":"mean"}).reset_index()
fig = make_subplots(rows=3, cols=5,
subplot_titles=([df_jejusi["Dong"].values[i] + "(" + str(i+1) + '위)' for i in range(15)]))
i = 0
for dong in df_jejusi["Dong"].values:
row = (i // 5) + 1
col = (i % 5) + 1
df_dong = df[(df["Gu"] == "제주시")& (df["Dong"] == dong)].sort_values(by='TotalSpent', ascending=False)[:5]
fig.add_trace(
go.Bar(x=df_dong["Type"], y=df_dong["TotalSpent"],
name="제주시" + dong + "업종별 소비금액",
marker=dict(color=colors[i])),
row, col,
)
i += 1
fig.update_layout(height=900, width=1000, title_text="제주시 동별 소비금액 상위 5위 업종")
fig.update_layout(showlegend=False)
fig.show()
해당 분석을 통해 앞서 보았던 소비 상위 15곳 동에 어떤 업종이 중점적으로 운영되고 어떤 업종이 입점해 있는지 유추해 볼 수 있음.
fig = make_subplots(rows=3, cols=5,
subplot_titles=([df_seogui["Dong"].values[i] + "(" + str(i+1) + '위)' for i in range(15)]))
i = 0
for dong in df_seogui["Dong"].values:
row = (i // 5) + 1
col = (i % 5) + 1
df_dong = df[(df["Gu"] == "서귀포시")& (df["Dong"] == dong)].sort_values(by='TotalSpent', ascending=False)[:5]
fig.add_trace(
go.Bar(x=df_dong["Type"], y=df_dong["TotalSpent"],
name="서귀포시" + dong + "업종별 소비금액",
marker=dict(color=colors[i])),
row, col,
)
i += 1
fig.update_layout(height=900, width=1000, title_text="서귀포시 동별 소비금액 상위 5위 업종")
fig.update_layout(showlegend=False)
fig.show()
주어진 제주도의 소비 데이터에 관해
이 같은 분석을 통해, 추후 재난지원금 배포 방법을 단순한 범국민 지급이 아닌 새롭게 접근 해볼수 있음.
예시)
from IPython.display import Image
display(Image(filename='real_estate_keywords.png', width = 500, height = 500))
들어가기 전에 KB부동산 Liiv ON 에서 친절히 정리해준 올해 부동산 내용을 보면...
출처: https://www.mk.co.kr/news/realestate/view/2020/12/1305822/
위와 같은 트랜드가 제주도에서도 연관이 있을까...?
도로교통부 실거래가 공개시스템 이용(http://rtdown.molit.go.kr/)
# thousands=',' 파라매터를 통한 string => integer 변환
# 데이터 로딩
df_apart_maemae = pd.read_csv("./아파트(매매)__실거래가_20201222224816.csv", encoding='cp949', thousands = ',')
# 데이터 체크 1
df_apart_maemae.head(2)
#데이터 체크 2
df_apart_maemae.info()
def filter_count_avgprice(df, location):
"""
df = dataframe to filter from
location = string of location to search within 시군구
"""
df_filter_1 = df[df['시군구'].apply(lambda x: location in x)]
df_filter_2 = df_filter_1.groupby(['계약년월']).agg(
{'전용면적(㎡)' : 'sum', '거래금액(만원)' : 'sum', '층':'count'}
).reset_index().rename(columns={'층': (location +'_count')})
df_filter_2[('면적금액평균_'+location)] = df_filter_2['거래금액(만원)'] / df_filter_2['전용면적(㎡)']
df_filter_2[('매매평균면적_'+location)] = df_filter_2['전용면적(㎡)'] / df_filter_2[(location +'_count')]
return df_filter_2
# 제주시의 매매 아파트 기준 월별 면적 별 거래 금액 평균, 매매 수, 매매 면적 평균을 먼저 구해봅니다
maemae_wholejeju = filter_count_avgprice(df_apart_maemae, '제주')
# 제주시와 서귀포시만 필터해서 월별 면적 당 거래 금액 평균, 매매 수, 매매 면적 평균을 구해봅니다
maemae_jejusi = filter_count_avgprice(df_apart_maemae, '제주시')
maemae_seguisi = filter_count_avgprice(df_apart_maemae, '서귀포시')
# 제주도의 아파트 값이 꾸준하게 오르는 추이를 보여주고, 면적 또한 커지는 추세, 매매 건수도 증가하는 추세입니다
# 제주시에 평균적으로 서귀포시 보다 매매수가 더 많고, 면적도 더 크네요. 면적별매매가도 더 비쌉니다
jeju_maemae_summary = pd.concat(
[
maemae_wholejeju[['계약년월','제주_count', '면적금액평균_제주', '매매평균면적_제주']],
maemae_jejusi[['제주시_count', '면적금액평균_제주시', '매매평균면적_제주시']],
maemae_seguisi[['서귀포시_count','면적금액평균_서귀포시', '매매평균면적_서귀포시']]
] , axis = 1)
# convert to datetime
jeju_maemae_summary['계약년월'] = jeju_maemae_summary['계약년월'].apply(lambda x : datetime.strptime(str(x), '%Y%m').date())
jeju_maemae_summary.head(2)
# 그래프 그리기 위한 함수 정의
def plot_summary(df_summary,feature, name):
# 한글 안깨지게 하기
font_name = font_manager.FontProperties(
fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)
plt.rcParams['axes.unicode_minus'] = False
#월별 면적당 평균 매매금액 그래프
fig = plt.figure(figsize=(13, 18))
ax1 = fig.add_subplot(211)
ax1.plot(
df_summary['계약년월'],
df_summary[feature + '_제주'],
linewidth = 3
)
ax1.plot(
df_summary['계약년월'],
df_summary[feature + '_제주시'])
ax1.plot(
df_summary['계약년월'],
df_summary[feature + '_서귀포시'])
#regression line for 제주 전체
x = np.array(list(range(12)))
m, b = np.polyfit(x, df_summary[feature + '_제주'], 1)
ax1.plot(df_summary['계약년월'],
m*x+b,
linewidth = 3,
linestyle = '--',
alpha = 0.5)
# style details
max_y = max(df_summary[feature + '_제주시']) + 20
min_y = min(df_summary[feature + '_서귀포시']) - 20
ax1.set_ylim(min_y, max_y)
ax1.set(xlabel='계약년월', ylabel= feature + '(만원)',
title = '20년 월별'+ name + '평균가')
ax1.legend(labels = ['제주전체', '제주시', '서귀포시', '제주전체 회귀'])
#월별 매매건수 그래프
ax2 = fig.add_subplot(212)
ax2.plot(
df_summary['계약년월'],
df_summary['제주_count'],
linewidth = 3
)
ax2.plot(
df_summary['계약년월'],
df_summary['제주시_count'])
ax2.plot(
df_summary['계약년월'],
df_summary['서귀포시_count'])
#regression line for 제주 전체
x = np.array(list(range(12)))
m, b = np.polyfit(x, df_summary['제주_count'], 1)
ax2.plot(df_summary['계약년월'],
m*x+b,
linewidth = 3,
linestyle = '--',
alpha = 0.5)
# style details
max_y = max(df_summary['제주_count']) + 20
min_y = min(df_summary['서귀포시_count']) - 20
ax2.set_ylim(min_y, max_y)
ax2.set(xlabel='계약년월', ylabel='월' + name + '건수',
title = '20년 월별' + name + '건수')
ax2.legend(labels = ['제주전체', '제주시', '서귀포시', '제주전체 회귀'])
#plt.style.available <= to check styles
plt.style.use('seaborn')
plt.show()
return None
plot_summary(jeju_maemae_summary, '면적금액평균', '매매')
[제주도 월별 면적당 매매가는 1년간 약 9%(월평균 1%) 증가하며 10% 중반대의 서울 수도권에 비해 낮지만 상승세는 존재]
[제주도 내 아파트 매매건수는 월평균 5% 씩 상승하며, 수요 증가에 의한 매매가 상승 효과 존재]
# 아파트 전세값도 한번 알아봅니다...
df_apart_junwolsae = pd.read_csv("./아파트(전월세)_실거래가_20201222173817.csv", encoding='cp949', thousands = ',')
#데이터 체크2
df_apart_junwolsae.info()
df_apart_junwolsae.head(2)
# 제주, 제주시, 서귀포시에 따라 데이터를 정리해주는 함수
def filter_count_avgprice_junwolsae(df, location):
"""
df = dataframe to filter from
location = string of location to search within 시군구
"""
df_filter_1 = df[df['시군구'].apply(lambda x: location in x)]
df_filter_2 = df_filter_1.groupby(['계약년월']).agg(
{'전용면적(㎡)' : 'sum', '보증금(만원)' : 'sum', '월세(만원)' : 'sum' ,'층':'count'}
).reset_index().rename(columns={'층': (location +'_count')})
df_filter_2[('면적별보증금평균_'+location)] = df_filter_2['보증금(만원)'] / df_filter_2['전용면적(㎡)']
df_filter_2[('면적별월세평균_'+location)] = df_filter_2['월세(만원)'] / df_filter_2['전용면적(㎡)']
df_filter_2[('평균면적_'+location)] = df_filter_2['전용면적(㎡)'] / df_filter_2[(location +'_count')]
return df_filter_2
# 전세로 월별 면적 별 보증금/월세 평균, 매매 수, 매매 면적 평균을 먼저 구해봅니다
# 전체적으로 전세 수가 줄어들고, 보증금 평균은 up and down이 많습니다
df_apart_junsae = df_apart_junwolsae[df_apart_junwolsae['전월세구분'] == '전세']
junsae_wholejeju = filter_count_avgprice_junwolsae(df_apart_junsae, '제주')
junsae_seguisi = filter_count_avgprice_junwolsae(df_apart_junsae, '서귀포시')
junsae_jejusi = filter_count_avgprice_junwolsae(df_apart_junsae, '제주시')
# 매매가와 똑같이 그래프를 위해 다 붙혀줍니다
jeju_junsae_summary = pd.concat(
[
junsae_wholejeju[['계약년월','제주_count', '면적별보증금평균_제주', '평균면적_제주']],
junsae_jejusi[['제주시_count', '면적별보증금평균_제주시', '평균면적_제주시']],
junsae_seguisi[['서귀포시_count','면적별보증금평균_서귀포시', '평균면적_서귀포시']]
] , axis = 1)
#동일하게 datetime 으로 바꿔줍니다
jeju_junsae_summary['계약년월'] = jeju_junsae_summary['계약년월'].apply(lambda x : datetime.strptime(str(x), '%Y%m').date())
jeju_junsae_summary.head(2)
plot_summary(jeju_junsae_summary, '면적별보증금평균', '전세')
[제주도 월별 면적당 전세가는 265만원 선을 맴돌면서 매매가 대비 큰 변화가 존재하지 않음]
[제주도 내 아파트 전세체결건수는 변화가 없는 전세가에 비해 월평균 7% 씩 감소하며 하락세]
display(Image(filename='jeju_land_price.png', width = 600, height = 500))
제주도 지역별 소비금액이 지역 부동산 가격의 현상/선행 지표가 될 수 있을까?
# 지역별 과 계약년월로 그룹핑을 해주고 지역당 면적평균 금액을 구합니다
df_apart_maemae_avg = df_apart_maemae.groupby(
['시군구', '계약년월']
).agg(
{'거래금액(만원)':'sum',
'전용면적(㎡)':'sum'}
).reset_index()
df_apart_maemae_avg['면적평균금액'] = df_apart_maemae_avg['거래금액(만원)'] / df_apart_maemae_avg['전용면적(㎡)']
# 데이터를 다루기 쉽게 피봇 테이블로 만들어줍니다
pivot_apart_maemae = df_apart_maemae_avg.pivot(
index = '시군구', columns = '계약년월', values = '면적평균금액'
).reset_index()
# 앞에 '제주특별자치도'는 빼줍니다
pivot_apart_maemae['시군구'] = pivot_apart_maemae['시군구'].apply(lambda x: x[8:])
####기본정보
# pivot_apart_maemae.columns
# Index([ '시군구', 201912, 202001, 202002, 202003, 202004, 202005, 202006, 202007, 202008, 202009, 202010, 202011],
# dtype='object', name='계약년월')
# len(pivot_apart_maemae['시군구'])
# 62
#그래프를 그릴 데이터만 따로 뽑아줍니다 (면적당 매매가 상위 10곳)
df_avg_202004 = pivot_apart_maemae[['시군구', 202004]].sort_values([202004], ascending=False).head(10)
df_avg_202008 = pivot_apart_maemae[['시군구', 202008]].sort_values([202008], ascending=False).head(10)
#면적당 매매가 하위 10곳
df_avg_202004_low = pivot_apart_maemae[['시군구', 202004]].sort_values([202004], ascending=False)[pivot_apart_maemae[202004].isna() == False].tail(10)
df_avg_202008_low = pivot_apart_maemae[['시군구', 202008]].sort_values([202008], ascending=False)[pivot_apart_maemae[202008].isna() == False].tail(10)
#한글 깨지는걸 방지
font_name = font_manager.FontProperties(
fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)
plt.rcParams['axes.unicode_minus'] = False
fig = plt.figure(figsize=(10, 12))
# 20년 08월 면적별매매평균가 상위 10곳
ax1 = fig.add_subplot(211)
ax1.barh(df_avg_202008['시군구'], df_avg_202008[202008], align='center')
ax1.invert_yaxis()
ax1.set_ylabel('시군구', fontsize=14, fontweight='bold')
ax1.set_xlabel('면적별매매가(만원/㎡)', fontsize=14, fontweight='bold')
ax1.set_title('\'20.08월 면적별평균가 상위 10곳', fontsize=18, fontweight = 'bold')
# 20년 08월 면적별매매평균가 하위 10곳
ax2 = fig.add_subplot(212)
ax2.barh(df_avg_202008_low['시군구'], df_avg_202008_low[202008], align='center')
ax2.invert_yaxis()
ax2.set_ylabel('시군구', fontsize=14, fontweight='bold')
ax2.set_xlabel('면적별매매가(만원/㎡)', fontsize=14, fontweight='bold')
ax2.set_title('\'20.08월 면적별평균가 하위 10곳', fontsize=18, fontweight = 'bold')
ax2.set_xlim(0, 600)
plt.style.use('seaborn')
plt.show()
['20.8월 기준 면적별평균가 1위는 제주시 이도이동(556만원/㎡), 하위 1위는 제주시 한리읍 협재리(161만원/㎡]
# 추후 지도에 그리기 위한 좌표 기록
# 좌표는 구글맵 기준, 동이름이 적혀있는곳의 좌표를 한땀한땀 옮겻습니다...ㅠㅠ
lowest_10_aug = ['제주시 삼도일동', '서귀포시 동홍동', '제주시 용담이동', '제주시 한립읍 한림리', '제주시 이도일동',
'제주시 삼도이동', '제주시 조천읍 신촌리', '서귀포시 성산읍 시흥리', '서귀포시 하원동', '제주시 한림읍 협재리']
lowest_10_aug_coor = [[33.50129, 126.52034], [33.26653, 126.56710], [33.51165, 126.51059], [33.41225, 126.27300],[33.50731, 126.52653],
[33.51188, 126.52194], [33.52560, 126.61541], [33.46905, 126.88563], [33.25769, 126.45970], [33.38150, 126.24816]]
df_lowest_10_aug = pd.DataFrame(data = lowest_10_aug_coor, columns = ['Latitude', 'Longitude'])
df_lowest_10_aug['시군구'] = lowest_10_aug
# 좌표 기록 2
highest_10_aug = ['제주시 아라일동', '서귀포시 강정동', '제주시 이도이동', '제주시 노형동', '제주시 월평동',
'제주시 화북이동', '서귀포시 서호동', '제주시 연동', '제주시 용담일동', '제주시 애월읍 하귀1리']
highest_10_aug_coor = [[33.47099, 126.54597], [33.25354, 126.49435], [33.49607, 126.53883], [33.46321, 126.47139],[33.46025, 126.57864],
[33.50544, 126.56598], [33.26991, 126.51622], [33.47682, 126.49314], [33.50859, 126.51444], [33.48020, 126.41602]]
df_highest_10_aug = pd.DataFrame(data = highest_10_aug_coor, columns = ['Latitude', 'Longitude'])
df_highest_10_aug['시군구'] = highest_10_aug
# 혹시 몰라 상승률을 다 구했었는데, 일단 8~11월만 써봅니다
pivot_apart_maemae['1~4월_상승률'] = (pivot_apart_maemae[202004] - pivot_apart_maemae[202001]) / pivot_apart_maemae[202001]
pivot_apart_maemae['4~8월_상승률'] = (pivot_apart_maemae[202008] - pivot_apart_maemae[202004]) / pivot_apart_maemae[202004]
pivot_apart_maemae['8~11월_상승률'] = (pivot_apart_maemae[202011] - pivot_apart_maemae[202008]) / pivot_apart_maemae[202008]
pivot_apart_maemae['1~11월_상승률'] = (pivot_apart_maemae[202011] - pivot_apart_maemae[202001]) / pivot_apart_maemae[202001]
# 상위 10곳, 하위 10곳으로 나눕니다
df_increase = pivot_apart_maemae[['시군구', '8~11월_상승률']].sort_values(['8~11월_상승률'], ascending=False).head(10)
df_decrease = pivot_apart_maemae[['시군구', '8~11월_상승률']].sort_values(['8~11월_상승률'], ascending=False)[pivot_apart_maemae['8~11월_상승률'].isna() == False].tail(10)
#한글 깨지는걸 방지
font_name = font_manager.FontProperties(
fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)
plt.rcParams['axes.unicode_minus'] = False
fig = plt.figure(figsize=(10, 12))
# 20년 08월 면적별매매평균가 상위 10곳
ax1 = fig.add_subplot(211)
ax1.barh(df_increase['시군구'], df_increase['8~11월_상승률'], align='center')
ax1.invert_yaxis()
ax1.set_ylabel('시군구', fontsize=14, fontweight='bold')
ax1.set_xlabel('8~11월_상승률', fontsize=14, fontweight='bold')
ax1.set_title('\'20.08~11월 면적별평균가 상승률 상위 10곳', fontsize=18, fontweight = 'bold')
# 20년 08월 면적별매매평균가 하위 10곳
ax2 = fig.add_subplot(212)
ax2.barh(df_decrease['시군구'], df_decrease['8~11월_상승률'], align='center')
ax2.invert_yaxis()
ax2.set_ylabel('시군구', fontsize=14, fontweight='bold')
ax2.set_xlabel('8~11월_상승률', fontsize=14, fontweight='bold')
ax2.set_title('\'20.08~11월 면적별평균가 상승률 하위 10곳', fontsize=18, fontweight = 'bold')
plt.style.use('seaborn')
plt.show()
['20.8월~11월 면적별평균매매가 상승률 1위는 제주시 도남동(41.9%), 하위 1위는 제주시 도련일동(-23.2%)]
#추후 지도 그래프에서 이용하기 위한 좌표 기록
highest_increase_coor = [[33.48893, 126.52520], [33.52243, 126.61955], [33.51082, 126.52164], [33.50545, 126.52760], [33.52140, 126.58358],
[33.47351, 126.54562], [33.26589, 126.56683], [33.47798, 126.47546], [33.26481, 126.55089], [33.25354, 126.49435]]
df_highest_increase = pd.DataFrame(data = highest_increase_coor, columns = ['Latitude', 'Longitude'])
df_highest_increase['시군구'] = df_increase.head(10).시군구.values
df_highest_increase['8~11월_상승률'] = df_increase.head(10)['8~11월_상승률'].values
#추후 지도 그래프에서 이용하기 위한 좌표 기록
highest_decrease_coor = [[33.49607, 126.53883], [33.49093, 126.51066], [33.50287, 126.56504], [33.25122, 126.43531], [33.52779, 126.59508],
[33.51610, 126.49244], [33.51827, 126.54122], [33.24804, 126.56415], [33.51949, 126.56563], [33.50475, 126.58701]]
df_highest_decrease = pd.DataFrame(data= highest_decrease_coor, columns = ['Latitude', 'Longitude'])
df_highest_decrease['시군구'] = df_decrease.tail(10)['시군구'].values
df_highest_decrease['8~11월_상승률'] = df_decrease.tail(10)['8~11월_상승률'].values
# 5월 소비데이터를 좌표로 그룹핑 해줍니다
df_spent_coor_05 = df5.groupby(['POINT_X', 'POINT_Y']).agg({'TotalSpent': 'sum', 'DisSpent' : 'sum'}).reset_index()
df_spent_coor_05 = df_spent_coor_05[df_spent_coor_05.TotalSpent.isna() == False].rename(columns = {'TotalSpent':'TotalSpent_05'})
# 8월 소비데이터를 좌표로 그룹핑 해줍니다
df_spent_coor_08 = df8.groupby(['POINT_X', 'POINT_Y']).agg({'TotalSpent': 'sum', 'DisSpent' : 'sum'}).reset_index()
df_spent_coor_08 = df_spent_coor_08[df_spent_coor_08.TotalSpent.isna() == False].rename(columns = {'TotalSpent':'TotalSpent_08'})
# 그룹핑된 5월과 8월 데이터를 붙히고 차이를 구합니다
df_spent_coor = pd.merge(df_spent_coor_05, df_spent_coor_08, how='inner', on=['POINT_X', 'POINT_Y'])
df_spent_coor['TotalSpent_diff'] = df_spent_coor['TotalSpent_08'] - df_spent_coor['TotalSpent_05']
df_spent_coor.head()
# UTMK 좌표를 WGS84로 바꾸어줍니다
proj_UTMK = Proj(init = 'epsg:5178')
proj_WGS84 = Proj(init= 'epsg:4326')
transformer = Transformer.from_proj(proj_UTMK, proj_WGS84)
points = []
for index, item in df_spent_coor[['POINT_X', 'POINT_Y']].iterrows():
points.append((item.POINT_X, item.POINT_Y))
latlist, lnglist = [], []
for pt in transformer.itransform(points):
latlist.append(pt[1])
lnglist.append(pt[0])
df_spent_coor['Latitude'] = latlist
df_spent_coor['Longitude'] = lnglist
# 소비금액을 바로 쓰면 차이가 너무 크기 때문에 정규화 후 로그를 시켜줍니다
# 감소 분까지 가져갈수 있도록 최소값을 더해줍니다
std_spent_diff = (df_spent_coor.TotalSpent_diff.values - np.mean(df_spent_coor.TotalSpent_diff.values)) / np.std(df_spent_coor.TotalSpent_diff.values)
std_spent_diff -= min(std_spent_diff)
df_spent_coor['log_TotalSpent_diff'] = np.log(std_spent_diff)
# 같은 작업입니다
std_spent=(df_spent_coor.TotalSpent_08.values - np.mean(df_spent_coor.TotalSpent_08.values)) / np.std(df_spent_coor.TotalSpent_08.values)
std_spent -= min(std_spent)
df_spent_coor['log_TotalSpent_08'] = np.log(std_spent)
#8월의 소비를 지역에 따라 그룹핑한 df_spent_coor 를 활용하여, 소비 히트맵을 그려봅니다
fig = px.density_mapbox(df_spent_coor, lat='Latitude', lon='Longitude', z='log_TotalSpent_08', radius=4,
center=dict( lat=df_spent_coor.Latitude.median(),
lon=df_spent_coor.Longitude.median()),
mapbox_style="open-street-map"
)
#상위/하쉬 10 지역을 같이 그려줍니다
fig.add_trace(go.Scattermapbox(lat=df_highest_10_aug.Latitude, lon=df_highest_10_aug.Longitude,
opacity = 0.75,
marker = dict(size = 20,
color = 'coral',
#line = dict(color='DarkSlateGrey', width=2)
),
text = [df_highest_10_aug['시군구'][i] + ' '+ str(10-i) + '위' for i in range(10)],
name = '8월 면적당매매가 상위 10'
))
fig.add_trace(go.Scattermapbox(lat=df_lowest_10_aug.Latitude, lon=df_lowest_10_aug.Longitude,
opacity = 0.75,
marker = dict(size = 20,
color = 'limegreen',
#line = dict(color='DarkSlateGrey', width=2)
),
text = [df_lowest_10_aug['시군구'][i] + ' ' + str(10-i) + '위' for i in range(10)],
name = '8월 면적당매매가 하위 10'
))
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()
[소비가 높았던 '20.8월, 높은 소비는 높은 아파트값의 필요조건이나, 충분조건은 아니다]
fig = px.density_mapbox(df_spent_coor, lat='Latitude', lon='Longitude', z='log_TotalSpent_diff', radius=4,
center=dict( lat=df_spent_coor.Latitude.median(),
lon=df_spent_coor.Longitude.median()),
mapbox_style="open-street-map"
)
fig.add_trace(go.Scattermapbox(lat=df_highest_increase.Latitude, lon=df_highest_increase.Longitude,
opacity = 0.75,
marker = dict(size = 20,
color = 'orangered',
#line = dict(color='DarkSlateGrey', width=2)
),
text = [df_highest_increase['시군구'][i] + str(i+1) + '위' +'<br>' + str(round(df_highest_increase['8~11월_상승률'][i], 2)*100) +'%' for i in range(10)],
name = '8~11월 면적당매매가 상승 상위 10'
))
fig.add_trace(go.Scattermapbox(lat=df_highest_decrease.Latitude, lon=df_highest_decrease.Longitude,
opacity = 0.75,
marker = dict(size = 20,
color = 'royalblue',
#line = dict(color='DarkSlateGrey', width=2)
),
text = [df_highest_decrease['시군구'][i] + str(10-i) + '위' + '<br>' + str(round(df_highest_decrease['8~11월_상승률'][i], 2)*100) +'%' for i in range(10)],
name = '8~11월 면적당매매가 상승 하위 10'
))
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()
['20.5~8월 까지의 소비 변화량은 이후 11월 까지의 아파트값 변화에 대한 좋은 선행지표가 되지 못했다]
8~11월 부터 값이 오르거나 내린 아파트들은 20년 전체로 봐도 오르거나 내렸을까?
df_diff_dec = pivot_apart_maemae[pivot_apart_maemae['시군구'].apply(lambda x : x in df_highest_decrease.시군구.values)]
df_diff_inc = pivot_apart_maemae[pivot_apart_maemae['시군구'].apply(lambda x : x in df_highest_increase.시군구.values)]
#한글 깨지는걸 방지
font_name = font_manager.FontProperties(
fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)
plt.rcParams['axes.unicode_minus'] = False
fig = plt.figure(122, figsize=(15, 6))
# 8~11월 면적당 매매가 상승 상위 10 지역, 1~11월 면적당 매매가 상승률과 비교
ax1 = fig.add_subplot(121)
ax1.bar(df_diff_inc.시군구, df_diff_inc['8~11월_상승률']*100, 0.25)
ax1.bar(np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + 0.25, df_diff_inc['1~11월_상승률']*100, 0.25)
ax1.set_xticks(np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + 0.25)
ax1.tick_params(labelrotation=45)
ax1.legend(labels = ['8~11월 상승률', '1~11월 상승률'])
ax1.set_ylabel('상승률(백분위)', fontsize=12, fontweight='bold')
ax1.set_xlabel('시군구', fontsize=12, fontweight='bold')
ax1.set_title('상위 10곳 8~11월 상승률 vs 1~11월 상승률', fontsize=18, fontweight = 'bold')
# '20년 04월과 20년 08월 면적별매매평균가 하위 10곳
ax2 = fig.add_subplot(122)
ax2.bar(df_diff_dec.시군구, df_diff_dec['8~11월_상승률']*100, 0.25)
ax2.bar(np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + 0.25, df_diff_dec['1~11월_상승률']*100, 0.25)
ax2.set_xticks(np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + 0.25)
ax2.tick_params(labelrotation=45)
ax2.legend(labels = ['8~11월 상승률', '1~11월 상승률'])
ax2.set_ylabel('상승률(백분위)', fontsize=12, fontweight='bold')
ax2.set_xlabel('시군구', fontsize=12, fontweight='bold')
ax2.set_title('하위 10곳 8~11월 상승률 vs 1~11월 상승률', fontsize=18, fontweight = 'bold')
plt.show()
[\'20.5~8월 까지의 아파트값 변화는 어느 정도 20.1~11월 전체의 변화를 대표한다]